package isssr.bean.control;

import isssr.bean.entity.GoalStatus;
import isssr.bean.entity.OrganizationalGoal;
import isssr.bean.entity.Role;
import isssr.bean.entity.User;
import isssr.bean.exception.EntityNotLoadedException;
import isssr.bean.exception.GoalElicitationException;
import isssr.bean.exception.IllegalUpdateException;
import isssr.bean.exception.InvalidPermissionException;
import isssr.bean.exception.NoEntityFoundException;
import isssr.bean.exception.NoMetaEntityIDException;
import isssr.bean.exception.NoOpenSessionException;
import isssr.bean.exception.PermissionType;
import isssr.bean.exception.StorageConflictException;
import isssr.bean.exception.StorageFailureException;
import isssr.bean.utility.StorageUtility;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.faces.context.FacesContext;
import javax.persistence.PersistenceException;

import org.hibernate.HibernateException;
import org.hibernate.exception.ConstraintViolationException;

/**
 * @author Ludovico
 *
 */
public class ActivityGOControl implements Serializable {

	/**
	 * 
	 */
	private static final long 	serialVersionUID = 182650091028037109L;
	
	private GoalElicitationException getGenericException(Exception ex) throws GoalElicitationException
	{
		String message = "";
		
		if(ex==null)
		{
			message = "An unknown error occurred. If this error recurred more than once, contact the Admin";
		}
		else
		{
			if(ex instanceof EntityNotLoadedException)
			{
				message =  "There's been a Loading error. If Problem persists, contact Administrator";
			}
			else if(ex instanceof StorageFailureException)
			{
				message =  "There's been a Storage error. If Problem persists, contact Administrator";
			}
			else if(ex instanceof Exception)
			{
				message = "An unknown error occurred. If this error recurred more than once, contact the Admin";
			}
		}

		GoalElicitationException ge = new GoalElicitationException(message);
		ge.initCause(ex);
		return ge;
	}
	
	private GoalElicitationException getUserAuthException(Exception ex) throws GoalElicitationException
	{
		String message = "";
		
		if(ex==null)
		{
			getGenericException(ex);
		}
		else
		{
			if(ex instanceof NoMetaEntityIDException)
			{
				message = "You're not authenticated. Please redo the login";
			}
			else if(ex instanceof NoEntityFoundException)
			{
				message = "You seem to be not registered. Please contact the Admin";
			}
			else if(ex instanceof InvalidPermissionException)
			{
				InvalidPermissionException ip = InvalidPermissionException.class.cast(ex);
				message = "You don't have the right permissions: " + ip.getPermissionType();
			}
		}

		GoalElicitationException ge = new GoalElicitationException(message);
		ge.initCause(ex);
		return ge;
	}
	

	private GoalElicitationException getOrgGoalException(Exception ex) throws GoalElicitationException
	{
		String message = "";
		
		if(ex==null)
		{
			getGenericException(ex);
		}
		else
		{
			if(ex instanceof NoMetaEntityIDException)
			{
				message = "Error in Goal Loading/Updating.";
			}
			if(ex instanceof NoEntityFoundException)
			{
				message = "Organizational Goal requested has not been found";
			}
			if(ex instanceof StorageConflictException)
			{
				message = "Some fields have conflicting values. Review your fields or contact Admin.";
			}
		}

		GoalElicitationException ge = new GoalElicitationException(message);
		ge.initCause(ex);
		return ge;
	}
	
	private GoalElicitationException throwFieldUpdateError(Exception ex) throws GoalElicitationException
	{
		String message = "";
		
		if(ex==null)
		{
			getGenericException(ex);
		}
		else
		{
			if(ex instanceof IllegalUpdateException)
			{
				message = "An illegal update action has been attempted. Check you parameters and retry";
			}
		}

		GoalElicitationException ge = new GoalElicitationException(message);
		ge.initCause(ex);
		return ge;
	}
	
	private void verifyGoalCreationPermission(User ownerWannabe)
	{
		Role role = ownerWannabe.getRole();
		
	}
	
	/*
	private void verifySuperLevel(OrganizationalGoal updatedGoal) throws IllegalUpdateException {
		
		if(updatedGoal.getSuperList() == null || updatedGoal.getSuperList().size() == 0)
		{
			return;
		}
		
		Iterator<User> it = updatedGoal.getSuperList().iterator();
		Role ownerRole = updatedGoal.getGoalOwner().getRole();
		Role superRole;
		
		while(it.hasNext())
		{
			superRole = it.next().getRole();
			if(superRole.getLevel() < ownerRole.getLevel())
			{
				throw new IllegalUpdateException();
			}
		}
	}
	*/
	/*
	private void verifyEnactorLevel(OrganizationalGoal updatedGoal) throws IllegalUpdateException {

		if(updatedGoal.getGoalEnactor() == null)
		{
			return;
		}
		
		Role GORole = updatedGoal.getGoalOwner().getRole();
	
		if(updatedGoal.getGoalEnactor() != null)
		{
			Role GERole = updatedGoal.getGoalEnactor().getRole();
			if(GORole.getLevel() < GERole.getLevel())
			{
				throw new IllegalUpdateException();
			}
		}
		
	}
*/

	private void verifySameOwner(OrganizationalGoal originalGoal, OrganizationalGoal updatedGoal) throws IllegalUpdateException 
	{
		if(updatedGoal.getGoalOwner() == null || ! updatedGoal.getGoalOwner().equals(originalGoal.getGoalOwner()))
		{
			throw new IllegalUpdateException();
		}
	}

	protected void verifyFields(OrganizationalGoal originalGoal, OrganizationalGoal updatedGoal) throws IllegalUpdateException, GoalElicitationException
	{
		verifySameOwner(originalGoal, updatedGoal);
		verifyUniqueTitle(originalGoal, updatedGoal);
		verifyEnactorLevel(updatedGoal);
		verifySuperLevel(updatedGoal);
	}
	
	

	private void verifyEnactorLevel(OrganizationalGoal updatedGoal) throws IllegalUpdateException 
	{
		User enactor = updatedGoal.getGoalEnactor();
		if(enactor == null) return;
		
		Role enactorRole = enactor.getRole();
		Role ownerRole 	 = updatedGoal.getGoalOwner().getRole();
		if(enactorRole.getLevel() >= ownerRole.getLevel())
			throw new IllegalUpdateException();
	}

	private void verifySuperLevel(OrganizationalGoal updatedGoal) throws IllegalUpdateException 
	{
		Set<User> superList = updatedGoal.getSuperList();
		if(superList == null) return;
		
		
		Role ownerRole 	 = updatedGoal.getGoalOwner().getRole();
		Role superRole;
		for(User zuper : superList)
		{
			superRole = zuper.getRole();

			if(superRole.getLevel() < ownerRole.getLevel())
				throw new IllegalUpdateException();
		}
	}

	private void verifyUniqueTitle(OrganizationalGoal originalGoal,
			OrganizationalGoal updatedGoal) throws GoalElicitationException {
		
		String originalTitle = originalGoal.getTitle();
		String updatedTitle = updatedGoal.getTitle();
		
		if(originalTitle.equals(updatedTitle))
			return;
		
		this.isTitleAleradyUsed(updatedTitle);
		
	}

	protected void updateFields(OrganizationalGoal originalGoal,
			OrganizationalGoal updatedGoal) {
		
		originalGoal.setTitle				(updatedGoal.getTitle());
		originalGoal.setConstraints			(updatedGoal.getConstraints());
		originalGoal.setDescription			(updatedGoal.getDescription());
		originalGoal.setFocus				(updatedGoal.getFocus());
		originalGoal.setGoalEnactor			(updatedGoal.getGoalEnactor());
		originalGoal.setMagnitude			(updatedGoal.getMagnitude());
		originalGoal.setObject				(updatedGoal.getObject());
		originalGoal.setOrganizationalScope	(updatedGoal.getOrganizationalScope());
		originalGoal.setPriority			(updatedGoal.getPriority());
		originalGoal.setTimeframe			(updatedGoal.getTimeframe());
		originalGoal.setSuperList			(updatedGoal.getSuperList());
		originalGoal.setRelations			(updatedGoal.getRelations());
		
	}
	
	public ActivityGOControl() {}
	
	
	// return OrganizationalGoal created on success null on failure or exception
	public OrganizationalGoal createGoal(User owner) throws GoalElicitationException
	{	
	/*	
		try
		{
			verifyGoalCreationPermission(owner);
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
			throw getUserAuthException(ex);
		}
		*/
		Long sequence_number 	= new Long(0);
		String username 		= owner.getUsername();
		String goalTitle 		= "OrganizationalGoal_"+ username +  "_" + sequence_number;
		
		while(this.isTitleAleradyUsed(goalTitle))
		{
			sequence_number++;
			goalTitle = "OrganizationalGoal_"+ username +  "_" + sequence_number;
		}
		
		OrganizationalGoal goal = new OrganizationalGoal();
		goal.setGoalOwner(owner);
		goal.setTitle(goalTitle);
		goal.setEnumStatus(GoalStatus.DEFINITION);
		
		try
		{
			goal.save();
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
			throw getOrgGoalException(ex);
		}
		
		return goal;
	}
	
	private boolean isTitleAleradyUsed(String goalTitle) throws GoalElicitationException {
		
		OrganizationalGoal verifyGoal = new OrganizationalGoal();
		verifyGoal.setTitle(goalTitle);
		
		try
		{
			Iterator<OrganizationalGoal> it = (Iterator<OrganizationalGoal>) verifyGoal.loadSiblings();
			
			if(it.hasNext())
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		catch(Exception ex)
		{
			throw getGenericException(ex);
		}
	}

	public OrganizationalGoal readGoal(Integer goalID, User owner) throws GoalElicitationException
	{
		OrganizationalGoal goalToFind = new OrganizationalGoal();
		goalToFind.setId(goalID);
		
		try
		{
			goalToFind.load();
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
			throw getOrgGoalException(ex);
		}
		
		if(!owner.equals(goalToFind.getGoalOwner()))
		{
			String errorMessage = "Organizational Goal not found.";
			GoalElicitationException ge = new GoalElicitationException(errorMessage);
			throw ge;
		}

		return goalToFind;
	}
	
	public void updateGoal(OrganizationalGoal modifiedGoal, User owner) throws GoalElicitationException 
	{
		OrganizationalGoal storedGoal = new OrganizationalGoal();
		storedGoal.setId(modifiedGoal.getId());
		
		try
		{
			storedGoal.load();
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
			throw getOrgGoalException(ex);
		}
		
		if(!storedGoal.getGoalOwner().equals(owner))
		{
			String errorMessage = "Operation Aborted. You're not the Owner of the Goal.";
			GoalElicitationException ex = new GoalElicitationException(errorMessage);
			throw ex;
		}
		
		try
		{
			verifyFields(storedGoal, modifiedGoal);
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
			throw throwFieldUpdateError(ex);
		}
		
		updateFields(storedGoal, modifiedGoal);
		
		try
		{
			storedGoal.update();
		} catch(Exception ex)
		{
			ex.printStackTrace();
			throw getOrgGoalException(ex);
		}
		return;
	}
	

	public void deleteGoal(Integer goalID, User owner) throws GoalElicitationException 	
	{
		OrganizationalGoal goalToDelete = new OrganizationalGoal();
		goalToDelete.setId(goalID);
		
		/*
		try
		{
			goalToDelete = this.loadGoalByOwner(goalID, owner);
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
			throw getOrgGoalException(ex);
		}
		*/
		try
		{
			goalToDelete.unsave();
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
			throw getOrgGoalException(ex);
		}
		
		return;
	}
	
	
	public ArrayList<User> getElegibleSuperList(User owner) 
	{
		return StorageUtility.getEligibleSupers(owner);
	}

	public ArrayList<User> getElegibleEnactorList(User owner)
	{
		return StorageUtility.getEligibleEnactors(owner);
	}
	
	public String getSuggestedGoalName(String baseName, String userName)
	{
		String suggestedName = "";
		String separator = "_";
		
		if(baseName == null)
			suggestedName.concat("Organzational_Goal");
		else 
			suggestedName = baseName;
		
		
		if(userName != null)
		{
			suggestedName.concat(separator);
			suggestedName.concat(userName);
		}
		
		Integer randomNumber = new Integer((int)Math.random()*1000);
		{
			suggestedName.concat(separator);
			suggestedName.concat(randomNumber.toString());
		}
		
		return suggestedName;
	}
	
	private OrganizationalGoal loadGoalByOwner(String title, User owner) throws Exception
	{
		OrganizationalGoal goal = new OrganizationalGoal();
		
		goal.setTitle(title);
		goal.setGoalOwner(owner);
		
		Iterator<OrganizationalGoal> it = (Iterator<OrganizationalGoal>) goal.loadSiblings();
		
		if(!it.hasNext())
		{
			throw new NoEntityFoundException(goal.getClass().getName());
		}
		
		return it.next();
	}
	
	private User loadUserById(Integer userID) throws 	NoMetaEntityIDException, 
														NoEntityFoundException, 
														EntityNotLoadedException, 
														StorageFailureException
	{
		User user = new User();
		user.setId(userID);
		
		user.load();
		/*
		try
		{
			user.load();
		}
		catch(NoEntityFoundException ex)
		{
			UserNotFoundException unf = new UserNotFoundException(userID);
			unf.initCause(ex);
			throw unf;
		}
		catch(NoMetaEntityIDException ex)
		{
			UserNotFoundException unf = new UserNotFoundException(userID);
			unf.initCause(ex);
			throw unf;
		}
		catch(EntityNotLoadedException ex)
		{
			throw ex;
		}
		catch(NoOpenSessionException ex)
		{
			StorageFailureException sf = new StorageFailureException();
			sf.initCause(ex);
			throw sf;
		}
		*/
		return user;
	}
	
	
	
	
	
}
